﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Wyse.Server.Base;
using Wyse.Server.Config;
using System.Net;
using System.Net.Sockets;

namespace Wyse.Server.Game
{
    /// <summary>
    /// Server module for the game server
    /// </summary>
    public class GameServerModule : ServerModule
    {
        /// <summary>
        /// Ctor
        /// </summary>
        public GameServerModule()
            : base("Game server")
        {
        }

        /// <summary>
        /// Initializes the module
        /// </summary>
        public override void Init()
        {
            // load the configuration

            bool startGame = false;

            ConfigManager.AccessConfig(ca =>
            {
                mPort = ca.Config.GetValue<int>("listener.port");
                mConnectionLimit = ca.Config.GetValue<int>("listener.connectionLimit");

                startGame = ca.Config.GetValue<bool>("game.startDefault", false);
            });

            if (startGame)
            {
                // initialize a default game automatically

                CreateNewTable("default");
            }
        }

        /// <summary>
        /// Gets a game table with the specified name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public GameManager GetTable(String name)
        {
            lock (this)
            {
                GameManager gm;
                if (mGames.TryGetValue(name, out gm))
                    return gm;
                return null;
            }
        }

        /// <summary>
        /// Creates a new game table
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public GameManager CreateNewTable(String name)
        {
            lock (this)
            {
                if (mGames.ContainsKey(name))
                    throw new InvalidOperationException("Game already exists");

                GameManager gm = new GameManager(name, this);
                mGames[name] = gm;

                return gm;
            }
        }

        /// <summary>
        /// Generates an unique token identyfing a player-table combination
        /// </summary>
        /// <param name="table"></param>
        /// <returns></returns>
        public PlayerToken GeneratePlayerToken(PlayerState player, GameManager table)
        {
            PlayerToken token = new PlayerToken(player, table);

            mPlayerCache[token.ID] = token;
            return token;
        }

        /// <summary>
        /// Implementation responsible for the actual creation
        /// of listeners - to be overriden in concrete module classes
        /// </summary>
        /// <returns>an IEnumerable holding all the listeners</returns>
        protected override IEnumerable<Listener> CreateListenersImpl()
        {
            Listener listener = new Listener(new IPEndPoint(IPAddress.Any, mPort),
                new ContextFactory(this), "Game listener");
            if (mConnectionLimit != 0)
                listener.ConnectionLimit = mConnectionLimit;

            return new[] { listener };
        }

        /// <summary>
        /// Context factory for game contexts
        /// </summary>
        private class ContextFactory : IClientContextFactory
        {
            public ContextFactory(GameServerModule server)
            {
                mServer = server;
            }

            public ClientContext CreateContext(TcpClient client)
            {
                return new GameClientContext(client, mServer);
            }

            private GameServerModule mServer;
        }

        private Dictionary<String, GameManager> mGames = new Dictionary<string, GameManager>();
        private Dictionary<Guid, PlayerToken> mPlayerCache = new Dictionary<Guid, PlayerToken>();

        private int mConnectionLimit;
        private int mPort;
    }
}
